'use strict'

const http = require('http');
const fs = require('fs');
const fsp = fs.promises
const { EDOM } = require('constants');

/*------------------解析body数据----------------------------*/ 

/**
 * Content-Disposition:form-data;name='file';filename='xxx'
 * Content-Type:text/plain
 * @param {string} headerData 
 */

function parseBodyHeaders(headerData){
    let hlist = headerData.split('\r\n').filter(p=>p.length>0)
    
    //解析content-disposition,hlist[0]
    //按照;切分数组。选出从第二个元素开始的数组
    let csplit = hlist[0].split(';').slice(1);

    let filename
    let name
    for(let a of csplit){
        if(a.indexOf('filename=')===0){
            filename=a.substring(a.indexOf('=')+2,a.length-1)
        }else{
            name = a.substring(a.indexOf('=')+2,a.length-1)
        }
    }

    let headers = {}
    let ind=0
    let k
    for(let h of hlist){
        ind=h.indexOf(':')
        k=h.substring(0,ind).toLowerCase()
        headers[k]=h.substring(ind+1).trim();
    }
    return{
        filename,
        name,
        headers,
        contentType:headers['content-type']||''
    }
}


function parseBody(bodyData,headers){
    let ctype = headers['content-type']
    let bdy = ctype.substring(ctype.indexOf('=')+1)
    let crlf_bdy = `\r\n--${bdy}`
    let header_end_index = bodyData.indexOf('\r\n\r\n',
                                        crlf_bdy.length);
    let headerData = bodyData.toString('utf8',crlf_bdy.length,
                                        header_end_index);

    
    //解析文件头信息
    let hd= parseBodyHeaders(headerData)

    let file = {
        start:header_end_index+4,
        end:bodyData.indexOf(crlf_bdy,header_end_index),
        ...hd
    }
    file.length = file.end-file.start
    return file
}
//解析文件扩展名
function parseExtName(filename){
    if(filename.length<2){
        return ''
    }
    let nsplit = filename.split('.').filter(p=>p.length>0)
    if(nsplit.length<2) return ''
    return `.${nsplit[ nsplit.length-1]}`
}


/*------------------解析body数据-end----------------------------*/ 

let html = `
    <!DOCTYPE html>
    <html>
        <head> 
            <title>First HTTP</title>
        </head>
        <body>
            你好
        </body>
    </html>
`
/*
    .jpg  .jepg->image/jepg
    .png->image/png
    .gif->image/x-icon
    .txt->text/plain
    .json->text/json 或 application/json
    .xml->text/xml 或 application/xml
*/ 

let routerTable = {

    
GET:{
    '/':async (ctx)=>{
        res.setHeader('content-type','text/html;charset=utf-8')
        ctx.end(html)
    },
    '/test':async (ctx) =>{
        let status = parseInt(Math.random()*200)+199
        ctx.statusCode=status
        ctx.end('test end');
    },

    '/upload':async(ctx)=>{
        let stm = fs.createReadStream('./upload.html');
        stm.pipe(ctx.res,{end:false});
        stm.on('end',()=>{
           ctx.end();
        })
    },
},
POST:{   
         '/data': async (ctx)=>{
        let file = parseBody(ctx.rawBody,ctx.headers)
        console.log(file)
        if(!ctx.rawBody){
            ctx.end();
            return;
        }
        
            let fname = `${Date.now()}--${Math.random()}`+`${parseExtName(file.filename)}`;

            let fh = await fsp.open(fname,'w+');
            let fert = await fh.write(ctx.rawBody,file.start,file.length,0)
            //关闭打开的文件
            fh.close()
           // ctx.end(ctx.rawBody);
           ctx.end(`${fret.bytesWritten}`)
       
    }
}
       /* let bufferList = [];
        let bufferLength = 0;
        req.on('data',chunk=>{
            bufferLength+=chunk.length;
            bufferList.push(chunk)
        })
        let bodyData=null;
        req.on('end',()=>{
            if(bufferList.length>0){
                bodyData=Buffer.concat(bufferList,bufferLength);
                bufferList=[];
            }
            let file=parseBody(bodyData,req.headers)
            console.log(file)
            parseBody(bodyData,req.headers)
            if(bodyData){
                res.end(bodyData)
            }else{
                res.end();
            }
        })
    }
},*/
}


http.createServer( async(req,res)=>{
    let rm = routerTable[req.method]

    let usplit = req.url.split('?')
    let pathname = usplit[0]
    let querystring = usplit[1]||''
   if(!rm[pathname] || !rm){
        res.statusCode=404
        res.end('page not found')
        return
   }
   //ctx是context的缩写，意为请求上下文,就是一个打包了各种数据和方法的对象
   let ctx = {
       method:req.method,
       path:pathname,
       search:querystring,
       headers:req.headers,
       res:res,
       end:(data,encoding = 'utf8')=>{
           res.end(data,encoding)
       },
       write:(data,encoding='utf8')=>{
           res.write(data,encoding)
       },
       totalLength:0,
       rawBody:null,
       bufferList:[]
   }
   await new Promise((rv,rj)=>{
    if('PD'.indexOf(req.method[0]>=0)){
        req.on('data',chunk=>{
            ctx.bufferList.push(chunk)
            ctx.totalLength += chunk.length
        })
    }else{
        req.on('data',data=>{
          res.statusCode = 400
          res.end('bad req',()=>{
            req.destroy();
          })  
        })
    }
    let resolved = false;
    req.on('end',()=>{
        resolved = true;
        rv();
    })
    req.on('close',()=>{
        (!resolved) && rv();
    })
   })
   if(!res.writable || res.writableEnded){
       return
   }
   if(ctx.bufferList.length>0){
       ctx.rawBody=Buffer.concat(ctx.bufferList,ctx.totalLength)
       ctx.bufferList=[]
   }
   
   rm[pathname](ctx);
    
}).listen(3456);